package config;

import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import io.swagger.annotations.Api;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import properties.Swagger2Properties;
import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.Contact;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger.web.UiConfiguration;
import springfox.documentation.swagger.web.UiConfigurationBuilder;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import springfox.documentation.swagger2.configuration.Swagger2DocumentationConfiguration;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 自定义启动Swagger2注解
 *
 * @original_author: 翟永超 http://blog.didispace.com
 * @editer YoCiyy
 * @date: 2020/5/28
 */
@Configuration
@EnableSwagger2
@ConditionalOnProperty(name = "yoci.swagger.enabled", matchIfMissing = true)
@Import({
		Swagger2DocumentationConfiguration.class,
		BeanValidatorPluginsConfiguration.class
})
@EnableSwaggerBootstrapUI
@EnableConfigurationProperties(Swagger2Properties.class)
public class Swagger2Configuration {

	@Bean
	@ConditionalOnMissingBean
	public Swagger2Properties swagger2Properties() {
		return new Swagger2Properties();
	}

	@Bean
	@ConditionalOnMissingBean
	public Docket plateformApi(Swagger2Properties swagger2Properties) {

		// base-path处理
		// 当没有配置任何path的时候，解析/**
		if (swagger2Properties.getBasePackage().isEmpty()) {
			swagger2Properties.getBasePath().add("/**");
		}

		List<Predicate<String>> basePath = new ArrayList<>();
		for (String path : swagger2Properties.getBasePath()) {
			basePath.add(PathSelectors.ant(path));
		}
		// exclude-path处理
		List<Predicate<String>> excludePath = new ArrayList<>();
		for (String path : swagger2Properties.getExcludePath()) {
			excludePath.add(PathSelectors.ant(path));
		}
//		return new Docket(DocumentationType.SWAGGER_2)
//				.groupName("meGroup")
//				.apiInfo(apiInfo(swagger2Properties))
//				.select()
//				.apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
//				.paths(PathSelectors.any())
//				.build();
		return new Docket(DocumentationType.SWAGGER_2)
				.host(swagger2Properties.getHost())
				.securityContexts(Collections.singletonList(securityContext()))
				.apiInfo(apiInfo(swagger2Properties))
				.select()
				.apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
				.paths(Predicates.and(Predicates.not(Predicates.or(excludePath)), Predicates.or(basePath)))
				.build();
	}

	@Bean
	public UiConfiguration uiConfiguration(Swagger2Properties swagger2Properties) {
		return UiConfigurationBuilder.builder()
				.deepLinking(swagger2Properties.getUiConfig().getDeepLinking())
				.defaultModelExpandDepth(swagger2Properties.getUiConfig().getDefaultModelExpandDepth())
				.defaultModelRendering(swagger2Properties.getUiConfig().getDefaultModelRendering())
				.defaultModelsExpandDepth(swagger2Properties.getUiConfig().getDefaultModelsExpandDepth())
				.displayOperationId(swagger2Properties.getUiConfig().getDisplayOperationId())
				.displayRequestDuration(swagger2Properties.getUiConfig().getDisplayRequestDuration())
				.docExpansion(swagger2Properties.getUiConfig().getDocExpansion())
				.maxDisplayedTags(swagger2Properties.getUiConfig().getMaxDisplayedTags())
				.operationsSorter(swagger2Properties.getUiConfig().getOperationsSorter())
				.showExtensions(swagger2Properties.getUiConfig().getShowExtensions())
				.tagsSorter(swagger2Properties.getUiConfig().getTagsSorter())
				.validatorUrl(swagger2Properties.getUiConfig().getValidatorUrl())
				.build();
	}

	private ApiInfo apiInfo(Swagger2Properties swagger2Properties) {
		return new ApiInfoBuilder()
				// 标题
				.title(swagger2Properties.getTitle())
				// 描述
				.description(swagger2Properties.getDescription())
				// 版本
				.version(swagger2Properties.getVersion())
				// 许可证
				.license(swagger2Properties.getLicense())
				// 许可证URL
				.licenseUrl(swagger2Properties.getLicenseUrl())
				// 联系人信息
				.contact(new Contact(
						swagger2Properties.getContact().getName(),
						swagger2Properties.getContact().getUrl(),
						swagger2Properties.getContact().getEmail()))
				// 服务条款URL
				.termsOfServiceUrl(swagger2Properties.getTermsOfServiceUrl())
				.build();
	}

	/**
	 * 配置默认的全局鉴权策略的开关，以及通过正则表达式进行匹配；默认 ^.*$ 匹配所有URL 其中 securityReferences 为配置启用的鉴权策略
	 *
	 * @return
	 */
	private SecurityContext securityContext() {
		return SecurityContext.builder()
				.securityReferences(defaultAuth())
				.forPaths(PathSelectors.regex(swagger2Properties().getAuthorization().getAuthRegex()))
				.build();
	}

	/**
	 * 配置默认的全局鉴权策略；其中返回的 SecurityReference 中，reference 即为ApiKey对象里面的name，保持一致才能开启全局鉴权
	 *
	 * @return
	 */
	private List<SecurityReference> defaultAuth() {
		AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
		AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
		authorizationScopes[0] = authorizationScope;
		return Collections.singletonList(SecurityReference.builder()
				.reference(swagger2Properties().getAuthorization().getName())
				.scopes(authorizationScopes).build());
	}

}
